package edu.utd.text.game.tests;

import junit.framework.Assert;
import junit.framework.TestCase;
import edu.utd.text.game.domain.GameObject;
import edu.utd.text.game.domain.areas.DeadEnd;
import edu.utd.text.game.domain.areas.Direction;
import edu.utd.text.game.domain.areas.Room;
import edu.utd.text.game.domain.exceptions.DeadEndEncounteredException;
import edu.utd.text.game.domain.exceptions.DuplicateGameObjectException;
import edu.utd.text.game.domain.exceptions.GameObjectNotFoundException;
import edu.utd.text.game.domain.exceptions.InvalidDirectionException;
import edu.utd.text.game.domain.item.GameItem;
import edu.utd.text.game.domain.player.Player;
import edu.utd.text.game.tests.mocks.FakeAxe;
import edu.utd.text.game.tests.mocks.FakeKnife;
import edu.utd.text.game.tests.mocks.FakeShovel;

public class TestRoom extends TestCase {

	
	public void testRoom() {
		//create a room with no objects in it
		Room deadEndRoom = new Room("Dead End Room");
		//check that all exits default to dead end until we modify them
		Assert.assertTrue(deadEndRoom.getNorthExit() instanceof DeadEnd);
		Assert.assertTrue(deadEndRoom.getSouthExit() instanceof DeadEnd);
		Assert.assertTrue(deadEndRoom.getEastExit() instanceof DeadEnd);
		Assert.assertTrue(deadEndRoom.getWestExit() instanceof DeadEnd);
		Assert.assertTrue(deadEndRoom.getNorthEastExit() instanceof DeadEnd);
		Assert.assertTrue(deadEndRoom.getSouthEastExit() instanceof DeadEnd);
		Assert.assertTrue(deadEndRoom.getNorthWestExit() instanceof DeadEnd);
		Assert.assertTrue(deadEndRoom.getSouthWestExit() instanceof DeadEnd);
		
		//make sure the arraylist was initialized on construction
		Assert.assertTrue(deadEndRoom.getObjects() != null);
		Assert.assertTrue(deadEndRoom.getObjects().isEmpty());
		
		//put a game object in the room
		FakeShovel fakeShovel = new FakeShovel();
		try {
			deadEndRoom.addGameObject(fakeShovel);
		} catch (DuplicateGameObjectException dgox) {
			dgox.printStackTrace();
			fail("WRONG: Could not add a GameObject to a Room where we should be able to.");
		}
		System.out.println("Successfully added an object to a room!");
		
		//now try to put the same object in there
		boolean caughtTheExceptionCorrectly = false;
		try {
			deadEndRoom.addGameObject(fakeShovel);
		} catch (DuplicateGameObjectException dgox) {
			System.out.println("Correctly handled duplicate exception (same instance)");
			caughtTheExceptionCorrectly = true;
		}
		if(!caughtTheExceptionCorrectly) 
			fail("WRONG: Failed to catch a duplicate GameObject added (same object twice).");
		
		//now try to put a different instance but of the same type, into the room
		caughtTheExceptionCorrectly = false;
		try {
			deadEndRoom.addGameObject(new FakeShovel());
		} catch (DuplicateGameObjectException dgox) {
			System.out.println("Correctly handled duplicate exception (diff instance, same type)");
			caughtTheExceptionCorrectly = true;
		}
		if(!caughtTheExceptionCorrectly)
			fail("WRONG: Failed to catch a duplicate GameObject added (2 objs of same type).");
	
		//now try to add another (unique) object to the array - should work
		try {
			deadEndRoom.addGameObject(new FakeAxe());
		} catch (DuplicateGameObjectException dgox) {
			fail("WRONG: Encountered a duplicate GameObject exception where we should be allowed to add one (it was unique)");
		}
		
		//try to remove the (same instance of) the item we added earlier
		boolean removed = deadEndRoom.removeObjectFromRoom(fakeShovel);
		if(!removed) fail("WRONG: Could not remove the game item (same instance) that was previously added.");
		//is it really gone?
		System.out.println("After removing game object (same instance) ::");
		for(GameObject aGameObject : deadEndRoom.getObjects())
			System.out.println(aGameObject);
		
		//try to remove the type of item we previously added (FakeAxe)
		removed = deadEndRoom.removeObjectFromRoom(new FakeAxe());
		if(!removed) fail("WRONG: Could not remove the game item (diff instance, same type) that was previously added.");
		System.out.println("After removing game object (diff instance, same type) (should be none) ::");
		for(GameObject aGameObject : deadEndRoom.getObjects())
			System.out.println(aGameObject);
		
		System.out.println("CREATING A ROOM WORKS!");
	}
	
	public void testAddingAndRetrievingObjectsFromRoom() {
		
		
		Room bedRoom = new Room("Bedroom");
		try {
			//add a new unique object to a room
			bedRoom.addGameObject(new FakeShovel());
		} catch (DuplicateGameObjectException dgox) {
			dgox.printStackTrace();
			fail("WRONG: Could not add a new unique GameItem to a new clean Room"); 
		} 
		
		try {
			//Do not allow duplicate GameItems in the Room
			bedRoom.addGameObject(new FakeShovel());
			
		} catch (DuplicateGameObjectException dgox) {
			System.out.println("CAUGHT a duplicate while trying to add a non-unique GameObject to a room"); }
		
		try {		
			//retrieve an item - should get the item and if exists, remove the item from the room
			bedRoom.retrieveGameItem(new FakeShovel());	
		} catch (GameObjectNotFoundException gonfx) {
			gonfx.printStackTrace();
			fail("WRONG: Could not retrieve a game object from a room correctly");
		}
		
		try {
			//now try to retrieve an item that does not exist in the room
			bedRoom.retrieveGameItem(new FakeAxe());
		} catch (GameObjectNotFoundException gonfx) {
			System.out.println("CAUGHT a GameObjectNotFoundException when the GameObject did not exist in the room.");
		}
		
		System.out.println("ADDING TO AND RETRIEVING FROM THE ROOM WORKS!");
	}
	
	public void testContainsGameObjectOfType() {
		try {
			GameItem item = new FakeAxe();
			Room room = new Room("room");
			room.addGameObject(item);
			Assert.assertTrue(room.containsGameObjectOfType(item.getClass()));
			Assert.assertTrue(room.containsGameObjectOfType(FakeAxe.class));
			room.addGameObject(new FakeShovel());
			Assert.assertTrue(room.containsGameObjectOfType((new FakeShovel()).getClass()));
			Assert.assertTrue(room.containsGameObjectOfType((new FakeAxe()).getClass()));
		} catch (Exception e) {
			e.printStackTrace();
			fail("WRONG...something happened");
		}
	}
	
	public void testRetrieveGameItem() {
		Room room = new Room("room");
		try {
			room.addGameObject(new FakeAxe());
		} catch(Exception e) { 
			e.printStackTrace(); 
			fail("Something unexpected happened while adding a gameobject to a room");
		}
		try {
			GameItem item = room.retrieveGameItem(new FakeAxe());
			Assert.assertTrue(item.equals(new FakeAxe()));
			Assert.assertTrue(room.getObjects().contains(new FakeAxe()));
			Assert.assertTrue(room.containsGameObjectOfType(FakeAxe.class));
		} catch(Exception e) {
			e.printStackTrace();
			fail("WRONG: Exception encountered at retrieveGameItem() ...why?");
		}
	}
	
	
	//METHOD HAS BEEN REMOVED
//	public void testRetreieveGameItemOfType() {
//		try {
//			GameItem knife = new FakeKnife();
//			Room room = new Room("room");
//			room.addGameObject(knife);
//			Assert.assertTrue(room.containsGameObjectOfType(FakeKnife.class));
//			GameObject retrievedObject = room.retrieveGameItemOfType(knife.getClass());
//			Assert.assertTrue(retrievedObject instanceof GameItem && retrievedObject!=null);
//			Assert.assertTrue(room.containsGameObjectOfType(knife.getClass()));
//			room.addGameObject(new FakeAxe());
//			retrievedObject = room.retrieveGameItemOfType((new FakeAxe()).getClass());
//			Assert.assertTrue(retrievedObject instanceof GameItem && retrievedObject!=null);
//			Assert.assertTrue(room.containsGameObjectOfType(retrievedObject.getClass()));
//		} catch (Exception e) {
//			e.printStackTrace();
//			fail("WRONG: Exception encountered at retrieveGameItemOfType() ... why?");
//		}
//		
//	}
	
	public void testDeadEnds() {
		Player player = new Player();
		//make sure dead ends are created by default on initialization
		boolean handledException = false;
		try {
			Room room = new Room("room");
			player.setCurrentRoom(room);
			player.moveDirection(Direction.NORTH);
		} catch(DeadEndEncounteredException deex) {
			handledException = true;
		} catch(InvalidDirectionException idx) {
			idx.printStackTrace();
			fail("WRONG: Got an InvalidDirectionException where one was not expected");
		}
		Assert.assertTrue(handledException);
		
		handledException = false;
		try {
			Room room = new Room("room");
			player.setCurrentRoom(room);
			player.moveDirection(Direction.SOUTH);
		} catch(DeadEndEncounteredException deex) {
			handledException = true;
		} catch(InvalidDirectionException idx) {
			idx.printStackTrace();
			fail("WRONG: Got an InvalidDirectionException where one was not expected");
		}
		
		handledException = false;
		try {
			Room room = new Room("room");
			player.setCurrentRoom(room);
			player.moveDirection(Direction.EAST);
		} catch(DeadEndEncounteredException deex) {
			handledException = true;
		} catch(InvalidDirectionException idx) {
			idx.printStackTrace();
			fail("WRONG: Got an InvalidDirectionException where one was not expected");
		}
		
		handledException = false;
		try {
			Room room = new Room("room");
			player.setCurrentRoom(room);
			player.moveDirection(Direction.WEST);
		} catch(DeadEndEncounteredException deex) {
			handledException = true;
		} catch(InvalidDirectionException idx) {
			idx.printStackTrace();
			fail("WRONG: Got an InvalidDirectionException where one was not expected");
		}
		
		handledException = false;
		try {
			Room room = new Room("room");
			player.setCurrentRoom(room);
			player.moveDirection(Direction.NORTHEAST);
		} catch(DeadEndEncounteredException deex) {
			handledException = true;
		} catch(InvalidDirectionException idx) {
			idx.printStackTrace();
			fail("WRONG: Got an InvalidDirectionException where one was not expected");
		}
		Assert.assertTrue(handledException);
		
		handledException = false;
		try {
			Room room = new Room("room");
			player.setCurrentRoom(room);
			player.moveDirection(Direction.NORTHWEST);
		} catch(DeadEndEncounteredException deex) {
			handledException = true;
		} catch(InvalidDirectionException idx) {
			idx.printStackTrace();
			fail("WRONG: Got an InvalidDirectionException where one was not expected");
		}
		Assert.assertTrue(handledException);
		
		handledException = false;
		try {
			Room room = new Room("room");
			player.setCurrentRoom(room);
			player.moveDirection(Direction.SOUTHEAST);
		} catch(DeadEndEncounteredException deex) {
			handledException = true;
		} catch(InvalidDirectionException idx) {
			idx.printStackTrace();
			fail("WRONG: Got an InvalidDirectionException where one was not expected");
		}
		Assert.assertTrue(handledException);
		
		handledException = false;
		try {
			Room room = new Room("Room");
			player.setCurrentRoom(room);
			player.moveDirection(Direction.SOUTHWEST);
		} catch(DeadEndEncounteredException deex) {
			handledException = true;
		} catch(InvalidDirectionException idx) {
			idx.printStackTrace();
			fail("WRONG: Got an InvalidDirectionException where one was not expected");
		}
		Assert.assertTrue(handledException);
	}
	
	public void testEquals() {
		//should return equal
		Room room1 = new Room("room1");
		Room room2 = new Room("room1");
		if(!room1.equals(room2)) {
			fail("2 Rooms of same name were not considered equal - they should be equal");
		}
		//should return NOT equal
		room2 = new Room("room2");
		if(room1.equals(room2)) {
			fail("2 rooms that were different were interpreted as equal, they should NOT be equal");
		}
		
		//try with 2 diff instances but same name
		Room aRoom = new Room("room");
		Assert.assertTrue(aRoom.equals(new Room("room")));
	}
	
	
}
